package cn.xiaopengstack.infrastructure.persistent.repository;

import cn.xiaopengstack.domain.trade.model.aggregate.GroupBuyOrderRefundAggregate;
import cn.xiaopengstack.domain.trade.model.aggregate.LockOrderAggregate;
import cn.xiaopengstack.domain.trade.model.aggregate.SettlementOrderAggregate;
import cn.xiaopengstack.domain.trade.model.entity.*;
import cn.xiaopengstack.domain.trade.model.valobj.GroupBuyOrderListStatusEnum;
import cn.xiaopengstack.domain.trade.model.valobj.GroupBuyOrderStatusEnum;
import cn.xiaopengstack.domain.trade.model.valobj.NotifyTaskStatusEnum;
import cn.xiaopengstack.domain.trade.model.valobj.NotifyTypeEnum;
import cn.xiaopengstack.domain.trade.repository.ITradeRepository;
import cn.xiaopengstack.infrastructure.persistent.dao.IGroupBuyOrderDao;
import cn.xiaopengstack.infrastructure.persistent.dao.IGroupBuyOrderListDao;
import cn.xiaopengstack.infrastructure.persistent.dao.IGroupBuyOrderRefundDao;
import cn.xiaopengstack.infrastructure.persistent.dao.INotifyTaskDao;
import cn.xiaopengstack.infrastructure.persistent.dcc.DCCService;
import cn.xiaopengstack.infrastructure.persistent.po.GroupBuyOrder;
import cn.xiaopengstack.infrastructure.persistent.po.GroupBuyOrderList;
import cn.xiaopengstack.infrastructure.persistent.po.GroupBuyOrderRefund;
import cn.xiaopengstack.infrastructure.persistent.po.NotifyTask;
import cn.xiaopengstack.infrastructure.persistent.redis.IRedisService;
import cn.xiaopengstack.infrastructure.persistent.strategy.notify.AbstractNotifyStrategy;
import cn.xiaopengstack.infrastructure.persistent.strategy.notify.INoticeStrategyService;
import cn.xiaopengstack.infrastructure.persistent.strategy.notify.NotifyStrategyFactory;
import cn.xiaopengstack.types.enums.RedisKeyEnum;
import cn.xiaopengstack.types.enums.RedisLuaScriptEnum;
import cn.xiaopengstack.types.enums.ResponseCode;
import cn.xiaopengstack.types.exception.AppException;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author jiangyangang
 * 拼团订单数据传输层
 */
@Repository
@Slf4j
public class TradeRepository implements ITradeRepository {

    @Resource
    private IGroupBuyOrderListDao groupBuyOrderListDao;
    @Resource
    private IGroupBuyOrderDao groupBuyOrderDao;
    @Resource
    private IGroupBuyOrderRefundDao groupBuyOrderRefundDao;
    @Resource
    private INotifyTaskDao notifyTaskDao;
    @Resource
    private DCCService dccService;
    @Resource
    private NotifyStrategyFactory notifyStrategyFactory;
    @Resource
    private IRedisService redisService;

    @Override
    public Optional<MarketOrderEntity> queryUnPayMarketPayOrder(String userId, String outOrderId) {

        GroupBuyOrderList groupBuyOrderList = groupBuyOrderListDao.queryUnPayMarketPayOrder(userId, outOrderId);
        if (Objects.isNull(groupBuyOrderList)) {
            return Optional.empty();
        }

        GroupBuyOrder groupBuyOrder = groupBuyOrderDao.queryGroupBuyOrderByTeamId(groupBuyOrderList.getTeamId());
        if (Objects.isNull(groupBuyOrder)) {
            log.warn("查询拼团订单异常，存在拼团详情但无拼团订单, teamId={}", groupBuyOrderList.getTeamId());
            return Optional.empty();
        }

        MarketOrderEntity marketOrderEntity = MarketOrderEntity.builder()
                .outOrderNo(groupBuyOrderList.getOutTradeNo())
                .orderId(groupBuyOrderList.getOrderId())
                .userId(groupBuyOrderList.getUserId())
                .deductionPrice(groupBuyOrder.getDeductionPrice())
                .activityId(groupBuyOrder.getActivityId())
                .originalPrice(groupBuyOrder.getOriginalPrice())
                .statusEnum(GroupBuyOrderListStatusEnum.of(groupBuyOrder.getStatus()))
                .teamId(groupBuyOrder.getTeamId())
                .build();

        return Optional.of(marketOrderEntity);
    }

    @Override
    public Optional<ProgressEntity> queryGoodsGroupByProgress(String teamId) {
        // 完成支付，以及锁单时需要更新
        ProgressEntity progressEntityCache = redisService.getValue(RedisKeyEnum.trade_order_progress_cache.getKey().formatted(teamId));
        return Optional.of(progressEntityCache)
                .or(() -> {
                    Optional<GroupBuyOrderEntity> groupBuyOrderEntity = queryOrderByTeamId(teamId);
                    return groupBuyOrderEntity.map((entity) -> ProgressEntity.builder()
                            .teamId(teamId)
                            .lockCount(entity.getLockCount())
                            .targetCount(entity.getTargetCount())
                            .completeCount(entity.getCompleteCount())
                            .build());
                });

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MarketOrderEntity lockGroupBuyMarketOrder(LockOrderAggregate aggregate) throws Exception{
        // 实体准备
        UserEntity userEntity = aggregate.getUserEntity();
        PayDiscountEntity payDiscountEntity = aggregate.getPayDiscountEntity();
        PayActivityEntity payActivityEntity = aggregate.getPayActivityEntity();
        // 判断是否是首笔拼单
        String teamId = payActivityEntity.getTeamId();
        if (payActivityEntity.isFirstGroupBuy()) {
            // 是，新增order记录
            GroupBuyOrder groupBuyOrder = GroupBuyOrder.builder()
                    .groupEndTime(new Date(payActivityEntity.getEndTime()))
                    .activityId(payActivityEntity.getActivityId())
                    .groupStartTime(new Date(payActivityEntity.getStartTime()))
                    .channel(payDiscountEntity.getChannel())
                    .source(payDiscountEntity.getSource())
                    .goodsId(payDiscountEntity.getGoodsId())
                    .lockCount(1)
                    .completeCount(0)
                    .targetCount(payActivityEntity.getTargetCount())
                    .status(GroupBuyOrderStatusEnum.GROUPING.getStatus())
                    .deductionPrice(payDiscountEntity.getDeductionPrice())
                    .originalPrice(payDiscountEntity.getOriginalPrice())
                    .payPrice(payDiscountEntity.getOriginalPrice().subtract(payDiscountEntity.getDeductionPrice()))
                    .teamId(teamId)
                    .build();
            insertGroupBuyOrder(groupBuyOrder);
        } else {

            incrGroupBuyOrderLockCount(teamId);
        }
        String orderId = RandomStringUtils.randomNumeric(12);
        GroupBuyOrderList groupBuyOrderList = GroupBuyOrderList.builder()
                .activityId(payActivityEntity.getActivityId())
                .status(GroupBuyOrderListStatusEnum.INIT_LOCK.getStatus())
                .outTradeNo(payDiscountEntity.getOutTradeNo())
                .orderId(orderId)
                .channel(payDiscountEntity.getChannel())
                .source(payDiscountEntity.getSource())
                .goodsId(payDiscountEntity.getGoodsId())
                .teamId(teamId)
                .userId(userEntity.getUserId())
                .userType(0)
                .build();

        groupBuyOrderListDao.insertSelective(groupBuyOrderList);

        return MarketOrderEntity.builder()
                .outOrderNo(payDiscountEntity.getOutTradeNo())
                .statusEnum(GroupBuyOrderListStatusEnum.INIT_LOCK)
                .deductionPrice(payDiscountEntity.getDeductionPrice())
                .originalPrice(payDiscountEntity.getOriginalPrice())
                .userId(userEntity.getUserId())
                .orderId(orderId)
                .activityId(payActivityEntity.getActivityId())
                .build();
    }

    private void incrGroupBuyOrderLockCount(String teamId) {
        String key = RedisKeyEnum.trade_order_cache.getKey().formatted(teamId);
        RMap<String, Object> mapCache = redisService.getMap(key);

        RLock lock = mapCache.getLock(key + "_" + "lockCount");

        lock.lock(3, TimeUnit.SECONDS);
        try {
            boolean exists = mapCache.isExists();
            int status = groupBuyOrderDao.updateAddLockCount(teamId);
            if (status <= 0) {
                throw new AppException(ResponseCode.LOCK_ORDER_FAIL);
            }

            if (exists) {
                String lockCountStr = String.valueOf(mapCache.get("lockCount"));
                int lockCount = Integer.parseInt(lockCountStr);
                lockCount += 1;
                mapCache.put("lockCount", lockCount);
            }

        } finally {
            lock.unlock();
        }
    }

    private void decrGroupBuyOrderLockCount(String teamId) {
        String key = RedisKeyEnum.trade_order_cache.getKey().formatted(teamId);
        RMap<String, Object> mapCache = redisService.getMap(key);

        RLock lock = mapCache.getLock(key + "_" + "lockCount");

        lock.lock(3, TimeUnit.SECONDS);
        try {
            boolean exists = mapCache.isExists();

            if (exists) {
                String lockCountStr = String.valueOf(mapCache.get("lockCount"));
                int lockCount = Integer.parseInt(lockCountStr);
                lockCount--;
                mapCache.put("lockCount", lockCount);
            }

        } finally {
            lock.unlock();
        }
    }


    /**
     * 更新拼团订单的完成数状态
     * @param teamId
     */
    private void incrGroupBuyOrderCompleteCount(String teamId) {
        String key = RedisKeyEnum.trade_order_cache.getKey().formatted(teamId);
        RMap<String, Object> mapCache = redisService.getMap(key);

        RLock lock = mapCache.getLock(key);

        lock.lock(3, TimeUnit.SECONDS);
        try {
            boolean exists = mapCache.isExists();
            if (exists) {
                Integer completeCount = Integer.parseInt(String.valueOf(mapCache.get("completeCount")));
                completeCount++;
                mapCache.put("completeCount", completeCount);

                Integer lockCount = Integer.parseInt(String.valueOf(mapCache.get("lockCount")));
                lockCount--;
                mapCache.put("lockCount", lockCount);
            }

        } finally {
            lock.unlock();
        }
    }

    /**
     * 更新拼团订单的完成数状态
     * @param teamId
     */
    private void decrCompleteCountCache(String teamId) {
        String key = RedisKeyEnum.trade_order_cache.getKey().formatted(teamId);
        RMap<String, Object> mapCache = redisService.getMap(key);

        RLock lock = mapCache.getLock(key + "_" + "completeCount");

        lock.lock(3, TimeUnit.SECONDS);
        try {
            boolean exists = mapCache.isExists();
            if (exists) {
                Integer completeCount = Integer.parseInt(String.valueOf(mapCache.get("completeCount")));
                completeCount--;
                mapCache.put("completeCount", completeCount);
            }

        } finally {
            lock.unlock();
        }
    }

    /**
     * 更新拼团订单的完成数状态
     * @param teamId
     */
    private void decrCompleteCountAndStatusCache(String teamId, GroupBuyOrderStatusEnum groupBuyOrderStatusEnum) {
        // 更新完成数
        decrCompleteCountCache(teamId);

        // 更新状态
        updateOrderStatusCache(teamId, groupBuyOrderStatusEnum);
    }

    /**
     * 更新状态
     * @param teamId
     * @param groupBuyOrderStatusEnum
     */
    private void updateOrderStatusCache(String teamId, GroupBuyOrderStatusEnum groupBuyOrderStatusEnum) {
        // 更新状态
        String key = RedisKeyEnum.trade_order_cache.getKey().formatted(teamId);
        RMap<String, Object> mapCache = redisService.getMap(key);

        RLock lock = mapCache.getLock(key + "_" + "status");

        lock.lock(3, TimeUnit.SECONDS);
        try {
            boolean exists = mapCache.isExists();
            if (exists) {
                mapCache.put("status", groupBuyOrderStatusEnum.getStatus());
            }

        } finally {
            lock.unlock();
        }
    }


    /**
     * 更新拼团订单状态为完成
     * @param teamId
     */
    private void updateGroupBuyOrderCompleteStatus(String teamId){
        String key = RedisKeyEnum.trade_order_cache.getKey().formatted(teamId);
        RMap<String, Object> mapCache = redisService.getMap(key);
        RLock lock = mapCache.getLock(key + "_" + "status");
        lock.lock(3, TimeUnit.SECONDS);
        try {
            int updateOrderStatusResult = groupBuyOrderDao.updateCompleteStatus(teamId);
            if (updateOrderStatusResult <= 0) {
                throw new AppException(ResponseCode.TRADE_SETTLEMENT_ORDER_FAIL);
            }
            boolean exists = mapCache.isExists();
            if (exists) {
                mapCache.put("status", GroupBuyOrderStatusEnum.GROUP_COMPLETE.getStatus());
            }
        } finally {
            lock.unlock();
        }
    }


    /**
     * 插入GroupBuyOrder
     * @param groupBuyOrder
     * @throws Exception
     */
    private void insertGroupBuyOrder(GroupBuyOrder groupBuyOrder) throws Exception{
        groupBuyOrderDao.insertSelective(groupBuyOrder);

        // 新增缓存构建失败
        RMap<String, Object> mapCache = redisService.getMap(RedisKeyEnum.trade_order_cache.getKey().formatted(groupBuyOrder.getTeamId()));
        Map<String, String> describe = BeanUtils.describe(groupBuyOrder);

        for (Map.Entry<String, String> entry : describe.entrySet()) {
            if (Objects.isNull(entry.getValue())) {
                continue;
            }

            mapCache.put(entry.getKey(), entry.getValue());
        }

        long groupBuyOrderEndTime = groupBuyOrder.getGroupEndTime().toInstant().getEpochSecond();
        long nowTime = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
        mapCache.expire(Duration.of(groupBuyOrderEndTime - nowTime + 3600L, ChronoUnit.SECONDS));
    }

    @Override
    public Optional<GroupBuyOrderEntity> queryOrderByTeamId(String teamId) {

//        RMap<String, Object> entityMap = redisService.getMap(RedisKeyEnum.trade_order_cache.getKey().formatted(teamId));
//        if (entityMap.isExists()) {
//            return Optional.of(
//                    GroupBuyOrderEntity.builder()
//                            .deductionPrice((BigDecimal) entityMap.get("deductionPrice"))
//                            .originalPrice((BigDecimal) entityMap.get("originalPrice"))
//                            .payPrice((BigDecimal) entityMap.get("payPrice"))
//                            .activityId((Long) entityMap.get("activityId"))
//                            .channel((String) entityMap.get("channel"))
//                            .source((String) entityMap.get("source"))
//                            .targetCount((Integer) entityMap.get("targetCount"))
//                            .completeCount((Integer) entityMap.get("completeCount"))
//                            .lockCount((Integer) entityMap.get("lockCount"))
//                            .goodsId((String) entityMap.get("goodsId"))
//                            .status(GroupBuyOrderStatusEnum.of((Integer) entityMap.get("status")) )
//                            .teamId((String) entityMap.get("teamId"))
//                            .groupStartTime((Date) entityMap.get("groupStartTime"))
//                            .groupEndTime((Date) entityMap.get("groupEndTime"))
//                            .build()
//            );
//        } else {
            GroupBuyOrder groupBuyOrder = groupBuyOrderDao.queryGroupBuyOrderByTeamId(teamId);
            if (Objects.isNull(groupBuyOrder)) {
                return Optional.empty();
            }

            GroupBuyOrderEntity groupBuyOrderEntity = GroupBuyOrderEntity.builder()
                    .deductionPrice(groupBuyOrder.getDeductionPrice())
                    .activityId(groupBuyOrder.getActivityId())
                    .groupStartTime(groupBuyOrder.getGroupStartTime())
                    .groupEndTime(groupBuyOrder.getGroupEndTime())
                    .lockCount(groupBuyOrder.getLockCount())
                    .targetCount(groupBuyOrder.getTargetCount())
                    .completeCount(groupBuyOrder.getCompleteCount())
                    .originalPrice(groupBuyOrder.getOriginalPrice())
                    .payPrice(groupBuyOrder.getPayPrice())
                    .status(GroupBuyOrderStatusEnum.of(groupBuyOrder.getStatus()))
                    .channel(groupBuyOrder.getChannel())
                    .source(groupBuyOrder.getSource())
                    .goodsId(groupBuyOrder.getGoodsId())
                    .teamId(groupBuyOrder.getTeamId())
                    .build();
            return Optional.of(groupBuyOrderEntity);
//        }
    }

    @Override
    public boolean updateStatusOrderByTeamId(String teamId, GroupBuyOrderStatusEnum statusEnum) {

        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlementOrder(SettlementOrderAggregate aggregate) {
        MarketOrderEntity marketOrderEntity = aggregate.getMarketOrderEntity();
        UserEntity userEntity = aggregate.getUserEntity();
        GroupBuyOrderEntity groupBuyOrderEntity = aggregate.getGroupBuyOrderEntity();
        TradePaySuccessEntity tradePaySuccessEntity = aggregate.getTradePaySuccessEntity();

        // 更新详情状态
        int updateResult = groupBuyOrderListDao.updateSettlementStatus(marketOrderEntity.getOrderId(), userEntity.getUserId(), tradePaySuccessEntity.getOutTradeTime());
        if (updateResult <= 0) {
            throw new AppException(ResponseCode.TRADE_SETTLEMENT_ORDER_FAIL.getCode(), ResponseCode.TRADE_SETTLEMENT_ORDER_FAIL.getInfo());
        }

        int status = groupBuyOrderDao.updateCountBySettlement(groupBuyOrderEntity.getTeamId());
        if (status <= 0) {
            throw new AppException(ResponseCode.TRADE_SETTLEMENT_ORDER_FAIL);
        }


        // 拼团完成后写入回调任务记录表
        if (groupBuyOrderEntity.getTargetCount() - groupBuyOrderEntity.getCompleteCount() == 1) {

            updateGroupBuyOrderCompleteStatus(groupBuyOrderEntity.getTeamId());

            List<String> tradeNoList = groupBuyOrderListDao.queryOrderListOutTradeNoList(groupBuyOrderEntity.getTeamId());
            JSONObject payload = new JSONObject();
            payload.put("outTradeNoList", JSONArray.parseArray(tradeNoList.toString()));
            payload.put("teamId", groupBuyOrderEntity.getTeamId());

            AbstractNotifyStrategy<?> abstractNotifyStrategy = notifyStrategyFactory.getNoticeService(tradePaySuccessEntity.getNotifyTypeEnum(), "settlementTrade");
            NotifyTask notifyTask = NotifyTask.builder()
                    .notifyType(abstractNotifyStrategy.type().getType())
                    .activityId(groupBuyOrderEntity.getActivityId())
                    .teamId(groupBuyOrderEntity.getTeamId())
                    .notifyCount(0)
                    .notifyStatus(NotifyTaskStatusEnum.INIT.getCode())
                    .notifyConfig(JSONObject.toJSONString(abstractNotifyStrategy.getConfig()))
                    .notifyPayload(payload.toJSONString())
                    .createTime(new Date())
                    .updateTime(new Date())
                    .build();
            notifyTaskDao.insertSelective(notifyTask);
        }

        // 更新拼团订单完成数
        incrGroupBuyOrderCompleteCount(groupBuyOrderEntity.getTeamId());
    }

    @Override
    public boolean isSCBlack(String source, String channel) {
        return dccService.isSCBlackIntercept(source, channel);
    }

    @Override
    public List<NotifyTaskEntity> queryUnExecutedNotifyTaskList() {

        List<NotifyTask> notifyTasks = notifyTaskDao.queryUnExecutedNotifyTaskList();

        if (CollectionUtils.isEmpty(notifyTasks)) {
            return Collections.emptyList();
        }
        return notifyTasks.stream().map(task -> NotifyTaskEntity.builder()
                .activityId(task.getActivityId())
                .teamId(task.getTeamId())
                .notifyUrl(task.getNotifyUrl())
                .notifyCount(task.getNotifyCount())
                .notifyStatus(task.getNotifyStatus())
                .notifyPayload(task.getNotifyPayload())
                .notifyConfig(task.getNotifyConfig())
                .notifyTypeEnum(NotifyTypeEnum.valueOf(task.getNotifyType()))
                .build()).collect(Collectors.toList());
    }

    @Override
    public List<NotifyTaskEntity> queryUnExecutedNotifyTaskList(String teamId) {
        List<NotifyTask> notifyTasks = notifyTaskDao.queryUnExecutedNotifyTaskListByTeamId(teamId);

        if (CollectionUtils.isEmpty(notifyTasks)) {
            return Collections.emptyList();
        }

        return notifyTasks.stream().map(task -> NotifyTaskEntity.builder()
                .activityId(task.getActivityId())
                .teamId(task.getTeamId())
                .notifyUrl(task.getNotifyUrl())
                .notifyCount(task.getNotifyCount())
                .notifyStatus(task.getNotifyStatus())
                .notifyPayload(task.getNotifyPayload())
                .notifyConfig(task.getNotifyConfig())
                .notifyTypeEnum(NotifyTypeEnum.valueOf(task.getNotifyType()))
                .build()).collect(Collectors.toList());
    }

    @Override
    public int updateNotifySuccess(NotifyTaskEntity notifyTaskEntity) {
        return notifyTaskDao.updateStatusByTeamId(notifyTaskEntity.getTeamId(), NotifyTaskStatusEnum.FINISH.getCode());
    }

    @Override
    public int updateNotifyError(NotifyTaskEntity notifyTaskEntity) {
        return notifyTaskDao.updateStatusByTeamId(notifyTaskEntity.getTeamId(), NotifyTaskStatusEnum.FAIL.getCode());

    }

    @Override
    public int updateNotifyRetry(NotifyTaskEntity notifyTaskEntity) {
        return notifyTaskDao.updateStatusAndIncrCountByTeamId(notifyTaskEntity.getTeamId(), NotifyTaskStatusEnum.RETRY.getCode());
    }

    @Override
    public boolean occupyTeamStock(String teamId, Long activityId, Long targetCount, Long validTime) {
        try {
            // 兜底分段锁
            String teamStockKey = generateTeamStockKey(teamId, activityId);
            String recoveryTeamStockKey = generateRecoveryTeamStockKey(teamId, activityId);
            String stockSegmentLockKey = generateStockSegmentLockKey(teamStockKey);

            String luaScript = RedisLuaScriptEnum.trade_team_occupy_lua.getScript();
            Long result = redisService.luaEval(luaScript,
                    Arrays.asList(teamStockKey, recoveryTeamStockKey, stockSegmentLockKey),
                    Arrays.asList(String.valueOf(targetCount), String.valueOf(validTime)));

            if (result < 0L) {
                log.warn("组队库存扣减失败, 已达库存上限");
                return false;
            } else if (result == 0L) {
                log.info("组队库存加分段锁失败 {}", stockSegmentLockKey);
            }

            return true;
        } catch (Exception e) {
            log.error("组队库存扣减失败, 程序异常");
            return false;
        }

    }


    @Override
    public int tradeSettlementNotify(NotifyTaskEntity notifyTaskEntity) {

        // 判断通知类型
        NotifyTypeEnum notifyTypeEnum = notifyTaskEntity.getNotifyTypeEnum();
        INoticeStrategyService noticeService = notifyStrategyFactory.getNoticeService(notifyTypeEnum, "job");

        try {
            noticeService.executeNotice(NotifyStrategyFactory.DynamicContext.builder()
                            .notifyTaskEntity(notifyTaskEntity)
                    .build());
        } catch (AppException e) {
            log.error("交易结算通知失败!", e);
            return 0;
        }

        return 1;
    }

    @Override
    public Optional<GroupBuyOrderListEntity> queryOrderDetailByOrderId(String orderId) {

        return Optional.of(groupBuyOrderListDao.queryOrderListByOrderId(orderId)).map((orderList) -> GroupBuyOrderListEntity
                .builder()
                .orderId(orderList.getOrderId())
                .outTradeNo(orderList.getOutTradeNo())
                .outTradeTime(orderList.getOutTradeTime())
                .activityId(orderList.getActivityId())
                .status(GroupBuyOrderListStatusEnum.of(orderList.getStatus()))
                .source(orderList.getSource())
                .teamId(orderList.getTeamId())
                .userId(orderList.getUserId())
                .channel(orderList.getChannel())
                .goodsId(orderList.getGoodsId())
                .userType(orderList.getUserType())
                .build());
    }

    @Override
    @Transactional(rollbackFor = Exception.class, timeout = 5000)
    public void unlockOrder(GroupBuyOrderRefundAggregate groupBuyOrderRefundAggregate) {
        TradeRefundRequestEntity tradeRefundRequestEntity = groupBuyOrderRefundAggregate.getTradeRefundRequestEntity();
        GroupBuyOrderListEntity groupBuyOrderListEntity = groupBuyOrderRefundAggregate.getGroupBuyOrderListEntity();
        UserEntity userEntity = groupBuyOrderRefundAggregate.getUserEntity();
        String teamId = groupBuyOrderListEntity.getTeamId();
        String orderId = groupBuyOrderListEntity.getOrderId();
        Long activityId = groupBuyOrderListEntity.getActivityId();

        // 更新组队详情
        int updateResult = groupBuyOrderListDao.updateRefundStatus(orderId);
        if (updateResult <= 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }
        // 更新拼团队伍的锁定值
        int status = groupBuyOrderDao.updateLockCountByRefund(teamId);
        if (status <= 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }

        // 保存退款记录
        GroupBuyOrderRefund groupBuyOrderRefund = new GroupBuyOrderRefund();
        groupBuyOrderRefund.setRefundOperator(tradeRefundRequestEntity.getRefundOperator());
        groupBuyOrderRefund.setRefundReason(tradeRefundRequestEntity.getRefundReason());
        groupBuyOrderRefund.setOutOrderId(tradeRefundRequestEntity.getOutRefundOrderId());
        groupBuyOrderRefund.setTeamId(groupBuyOrderListEntity.getTeamId());
        groupBuyOrderRefund.setUserId(userEntity.getUserId());
        groupBuyOrderRefund.setCreateTime(new Date());
        groupBuyOrderRefund.setUpdateTime(new Date());
        groupBuyOrderRefund.setRefundOrderId(groupBuyOrderRefundAggregate.getRefundOrderId());
        groupBuyOrderRefund.setOrderId(groupBuyOrderListEntity.getOrderId());
        groupBuyOrderRefundDao.insertSelective(groupBuyOrderRefund);

        // 更新数据库和实体缓存
        decrGroupBuyOrderLockCount(teamId);

        // 更新库存缓存
        String teamStockKey = generateTeamStockKey(teamId, activityId);
        String luaScript = RedisLuaScriptEnum.trade_team_stock_refund_lua.getScript();
        Long result = redisService.luaEval(luaScript, List.of(teamStockKey), Collections.emptyList());
        if (result < 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, timeout = 5000)
    public void refundOrder(GroupBuyOrderRefundAggregate groupBuyOrderRefundAggregate) {
        TradeRefundRequestEntity tradeRefundRequestEntity = groupBuyOrderRefundAggregate.getTradeRefundRequestEntity();
        GroupBuyOrderListEntity groupBuyOrderListEntity = groupBuyOrderRefundAggregate.getGroupBuyOrderListEntity();
        UserEntity userEntity = groupBuyOrderRefundAggregate.getUserEntity();
        String teamId = groupBuyOrderListEntity.getTeamId();
        GroupBuyOrderStatusEnum groupBuyOrderStatusEnum = groupBuyOrderRefundAggregate.getGroupBuyOrderStatusEnum();

        // 更新数据库
        int detailStatus = groupBuyOrderListDao.updateRefundStatus(groupBuyOrderListEntity.getOrderId());
        if (detailStatus <= 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }

        // 拼团已经完成则更新状态为已完成-有退款并回退次数
        if (GroupBuyOrderStatusEnum.GROUP_COMPLETE == groupBuyOrderStatusEnum) {
            int status = groupBuyOrderDao.updateCompleteCountAndStatusByRefund(teamId, GroupBuyOrderStatusEnum.GROUP_COMPLETE_REFUND.getStatus());
            if (status <= 0) {
                throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
            }
        }

        // 其他情况均回退完成数即可
        else {
            int status = groupBuyOrderDao.updateCountByRefund(teamId);
            if (status <= 0) {
                throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
            }
        }

        // 保存退款记录
        GroupBuyOrderRefund groupBuyOrderRefund = new GroupBuyOrderRefund();
        groupBuyOrderRefund.setRefundOperator(tradeRefundRequestEntity.getRefundOperator());
        groupBuyOrderRefund.setRefundReason(tradeRefundRequestEntity.getRefundReason());
        groupBuyOrderRefund.setOutOrderId(tradeRefundRequestEntity.getOutRefundOrderId());
        groupBuyOrderRefund.setTeamId(groupBuyOrderListEntity.getTeamId());
        groupBuyOrderRefund.setUserId(userEntity.getUserId());
        groupBuyOrderRefund.setCreateTime(new Date());
        groupBuyOrderRefund.setUpdateTime(new Date());
        groupBuyOrderRefund.setRefundOrderId(groupBuyOrderRefundAggregate.getRefundOrderId());
        groupBuyOrderRefund.setOrderId(groupBuyOrderListEntity.getOrderId());
        groupBuyOrderRefundDao.insertSelective(groupBuyOrderRefund);

        // 更新库存缓存
        String teamStockKey = generateTeamStockKey(groupBuyOrderListEntity.getTeamId(), groupBuyOrderListEntity.getActivityId());
        String luaScript = RedisLuaScriptEnum.trade_team_stock_refund_lua.getScript();
        Long result = redisService.luaEval(luaScript, List.of(teamStockKey), Collections.emptyList());
        if (result < 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }

        // 更新实体缓存
        decrCompleteCountAndStatusCache(groupBuyOrderListEntity.getTeamId(), GroupBuyOrderStatusEnum.GROUP_COMPLETE_REFUND);

        // 保存退款通知记录
        JSONObject payload = new JSONObject();
        payload.put("orderId", groupBuyOrderListEntity.getOrderId());
        payload.put("outTradeNo", groupBuyOrderListEntity.getOutTradeNo());
        payload.put("userId", groupBuyOrderListEntity.getUserId());
        payload.put("outRefundOrderId", groupBuyOrderListEntity.getOutTradeNo());

        AbstractNotifyStrategy<?> notifyStrategy = notifyStrategyFactory.getNoticeService(NotifyTypeEnum.MQ, "refund");
        NotifyTask notifyTask = NotifyTask.builder()
                .notifyType(notifyStrategy.type().getType())
                .activityId(groupBuyOrderListEntity.getActivityId())
                .teamId(groupBuyOrderListEntity.getTeamId())
                .notifyCount(0)
                .notifyStatus(NotifyTaskStatusEnum.INIT.getCode())
                .notifyConfig(JSONObject.toJSONString(notifyStrategy.getConfig()))
                .notifyPayload(payload.toJSONString())
                .createTime(new Date())
                .updateTime(new Date())
                .build();
        notifyTaskDao.insertSelective(notifyTask);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, timeout = 5000)
    public void refundTeam(GroupBuyOrderRefundAggregate groupBuyOrderRefundAggregate) {
        TradeRefundRequestEntity tradeRefundRequestEntity = groupBuyOrderRefundAggregate.getTradeRefundRequestEntity();
        GroupBuyOrderListEntity groupBuyOrderListEntity = groupBuyOrderRefundAggregate.getGroupBuyOrderListEntity();
        UserEntity userEntity = groupBuyOrderRefundAggregate.getUserEntity();

        String teamId = groupBuyOrderListEntity.getTeamId();

        // 更新数据库
        int detailStatus = groupBuyOrderListDao.updateRefundStatus(groupBuyOrderListEntity.getOrderId());
        if (detailStatus <= 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }

        int status = groupBuyOrderDao.updateRefundAllTeam(teamId);
        if (status <= 0) {
            throw new AppException(ResponseCode.TRADE_SETTLEMENT_ORDER_FAIL);
        }

        // 保存退款记录
        GroupBuyOrderRefund groupBuyOrderRefund = new GroupBuyOrderRefund();
        groupBuyOrderRefund.setRefundOperator(tradeRefundRequestEntity.getRefundOperator());
        groupBuyOrderRefund.setRefundReason(tradeRefundRequestEntity.getRefundReason());
        groupBuyOrderRefund.setOutOrderId(tradeRefundRequestEntity.getOutRefundOrderId());
        groupBuyOrderRefund.setTeamId(groupBuyOrderListEntity.getTeamId());
        groupBuyOrderRefund.setUserId(userEntity.getUserId());
        groupBuyOrderRefund.setCreateTime(new Date());
        groupBuyOrderRefund.setUpdateTime(new Date());
        groupBuyOrderRefund.setRefundOrderId(groupBuyOrderRefundAggregate.getRefundOrderId());
        groupBuyOrderRefundDao.insertSelective(groupBuyOrderRefund);

        // 更新库存缓存
        String teamStockKey = generateTeamStockKey(groupBuyOrderListEntity.getTeamId(), groupBuyOrderListEntity.getActivityId());
        String luaScript = RedisLuaScriptEnum.trade_team_stock_refund_lua.getScript();;
        Long result = redisService.luaEval(luaScript, List.of(teamStockKey), Collections.emptyList());
        if (result < 0) {
            throw new AppException(ResponseCode.TRADE_REFUND_FAIL);
        }

        // 更新数据库和实体缓存
        decrCompleteCountAndStatusCache(groupBuyOrderListEntity.getTeamId(), GroupBuyOrderStatusEnum.GROUP_FAIL);

        // 保存退款通知记录
        JSONObject payload = new JSONObject();
        payload.put("orderId", groupBuyOrderListEntity.getOrderId());
        payload.put("outTradeNo", groupBuyOrderListEntity.getOutTradeNo());
        payload.put("userId", groupBuyOrderListEntity.getUserId());
        payload.put("outRefundOrderId", groupBuyOrderListEntity.getOutTradeNo());

        AbstractNotifyStrategy<?> notifyStrategy = notifyStrategyFactory.getNoticeService(NotifyTypeEnum.MQ, "refund");
        NotifyTask notifyTask = NotifyTask.builder()
                .notifyType(notifyStrategy.type().getType())
                .activityId(groupBuyOrderListEntity.getActivityId())
                .teamId(groupBuyOrderListEntity.getTeamId())
                .notifyCount(0)
                .notifyStatus(NotifyTaskStatusEnum.INIT.getCode())
                .notifyConfig(JSONObject.toJSONString(notifyStrategy.getConfig()))
                .notifyPayload(payload.toJSONString())
                .createTime(new Date())
                .updateTime(new Date())
                .build();
        notifyTaskDao.insertSelective(notifyTask);
    }

    @Override
    public Optional<GroupBuyOrderRefundEntity> queryRefundByOrderId(String orderId, String userId) {
        GroupBuyOrderRefund groupBuyOrderRefund = groupBuyOrderRefundDao.queryRefundByOrderId(orderId, userId);
        if (Objects.isNull(groupBuyOrderRefund)) {
            return Optional.empty();
        }

        return Optional.of(GroupBuyOrderRefundEntity
                        .builder()
                        .refundOrderId(groupBuyOrderRefund.getRefundOrderId())
                        .refundReason(groupBuyOrderRefund.getRefundReason())
                        .refundOperator(groupBuyOrderRefund.getRefundOperator())
                        .outOrderId(groupBuyOrderRefund.getOutOrderId())
                        .userId(groupBuyOrderRefund.getUserId())
                        .teamId(groupBuyOrderRefund.getTeamId())
                        .build()
        );
    }


    private String generateStockSegmentLockKey(String teamStockKey) {
        return teamStockKey + "_";
    }

    private String generateRecoveryTeamStockKey(String teamId, Long activityId) {
        return String.format(RedisKeyEnum.trade_recover_team_stock_key.getKey(), activityId, teamId);
    }

    private String generateTeamStockKey(String teamId, Long activityId) {
        return String.format(RedisKeyEnum.trade_team_stock_key.getKey(), activityId, teamId);
    }

}
